iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0

今天我們要設計的線上投票系統,表面上只是點擊按鈕、累加數字這麼簡單,但深入探討後會發現,它涉及了分散式系統中最經典的幾個問題:資料一致性併發控制即時通訊,以及防作弊機制。透過這個系統的設計,我們將學習如何在不同規模下做出合適的架構決策。

場景定義與需求分析

業務場景描述

想像一個全國性的電視節目正在進行年度最受歡迎歌手投票,預計有數百萬觀眾參與。投票期間為期一週,每位用戶可以每天投一票給不同的歌手。主辦方希望觀眾能即時看到投票排名變化,增加節目的互動性和話題性。同時,系統必須能防止惡意刷票,確保投票結果的公正性。

這個場景代表了現代投票系統的典型需求:高併發、即時性、防作弊。無論是企業內部的決策投票、社區的意見調查,還是大型活動的觀眾投票,核心挑戰都是相似的。

系統的核心價值在於:

  • 資料準確性:每一票都必須被正確記錄,不能遺失或重複
  • 即時性:使用者期待看到實時更新的投票結果
  • 公正性:防止各種形式的作弊行為
  • 可擴展性:能夠應對從小型投票到大型活動的不同規模

核心需求分析

功能性需求:

  • 建立投票活動(設定選項、時間、規則)
  • 投票功能(單選、多選、排序投票)
  • 即時結果顯示(數字、百分比、圖表)
  • 投票歷史記錄查詢
  • 投票活動管理(開始、暫停、結束)
  • 結果匯出與報表生成
  • 使用者身份驗證(可選)

非功能性需求:

  • 效能要求:頁面載入時間 < 1秒,投票回應時間 < 500ms
  • 併發量:支援每秒 10,000 次投票請求
  • 可用性:99.9% SLA,關鍵投票活動期間不允許停機
  • 資料一致性:強一致性要求,不允許票數錯誤
  • 安全性:防止 SQL 注入、XSS 攻擊、CSRF 攻擊、DDos 攻擊
  • 防作弊:限制重複投票、機器人投票
  • 成本控制:根據使用量彈性擴展,避免資源浪費

核心架構決策

識別關鍵問題

技術挑戰 1:即時結果更新

投票結果需要即時反映給所有線上使用者,這涉及到伺服器如何推送資料給客戶端。傳統的輪詢方式會造成大量無效請求,WebSocket 雖然即時但維護成本較高,Server-Sent Events (SSE) 則是折衷方案。

技術挑戰 2:防止重複投票

如何識別並阻止同一使用者重複投票是系統設計的核心難題。單純依賴 IP 地址會誤傷同一網路的不同使用者,Cookie 容易被清除,而強制登入又會降低參與率。

技術挑戰 3:高併發下的計數準確性

當大量使用者同時投票時,如何確保票數統計的準確性?資料庫的鎖機制可能成為瓶頸,而快取層的引入又會帶來資料一致性的挑戰。

架構方案比較

維度 方案A:傳統單體架構 方案B:微服務架構 方案C:Serverless 架構
核心特點 所有功能在單一應用中 投票、統計、推送分離 事件驅動、按需擴展
優勢 開發簡單、部署方便 獨立擴展、故障隔離 零運維、成本最優
劣勢 擴展困難、單點故障 複雜度高、網路開銷 冷啟動、廠商鎖定
適用場景 小型投票、內部使用 中大型投票平台 不定期的投票活動
複雜度
成本 固定成本低 固定成本高 按使用付費

決策思考框架

diagram1

系統演進路徑

第一階段:MVP(0-10,000 / 使用者)

架構重點:

  • 單體應用快速開發
  • 關聯式資料庫儲存投票資料
  • Session 或 Cookie 防重複投票
  • 簡單的定時輪詢更新結果

系統架構圖:

diagram2

為什麼這樣設計:

在 MVP 階段,我們優先考慮快速上線和開發效率。選擇單體架構是因為它能讓小團隊快速迭代,不需要處理分散式系統的複雜性。使用 PostgreSQL 作為主要資料庫,因為它提供了 ACID 特性,確保投票資料的一致性。Redis 作為快取層,減少資料庫壓力並提供更快的讀取速度。

這個階段的防重複投票機制相對簡單,主要依賴 Session 或 Cookie。雖然不夠完美,但對於大多數正常使用者已經足夠。即時性方面,我們使用客戶端定時輪詢(例如每 5 秒一次),這在使用者量不大時是可接受的。

第二階段:成長期(10,000-100,000 / 使用者)

架構演進重點:

  • 引入 Server-Sent Events 實現真正的即時推送
  • 實施多層防重複投票機制
  • 資料庫讀寫分離
  • 引入訊息佇列處理投票請求

關鍵設計變更:

  1. 即時推送機制升級

    • 原因:輪詢方式在使用者增長後造成大量無效請求
    • 實施方式:採用 SSE 技術,伺服器主動推送更新
    • 預期效果:減少 80% 的無效請求,提升使用者體驗
  2. 防作弊機制強化

    • 原因:簡單的 Cookie 機制容易被繞過
    • 實施方式:結合 IP 限流、設備指紋
    • 預期效果:有效攔截 95% 以上的惡意投票
  3. 投票處理異步化

    • 原因:同步處理在高峰期造成回應延遲
    • 實施方式:投票請求進入訊息佇列,異步處理後更新結果
    • 預期效果:提升系統吞吐量 3-5 倍

系統架構圖:

diagram3

第三階段:規模化(100,000+ / 使用者)

企業級架構特點:

diagram4

架構設計考量:

  1. 高可用性設計

    • 多區域部署,任一區域故障不影響服務
    • 資料跨區域同步,確保一致性
    • 自動故障轉移機制,RTO < 1 分鐘
  2. 擴展性規劃

    • 水平擴展能力,可根據流量自動擴縮容
    • 資料分片策略,支援 PB 級資料儲存
    • 邊緣運算降低核心系統壓力
  3. 營運效率

    • 完整的監控體系,包含業務指標和技術指標
    • 自動化部署流程,藍綠部署確保零停機
    • A/B 測試框架,支援功能漸進式發布

技術選型深度分析

即時通訊技術比較

技術選項 優勢 劣勢 適用場景
長輪詢 實作簡單、相容性好 延遲較高、資源消耗大 小型應用、即時性要求不高
SSE 單向推送、自動重連 只支援文本、IE 不支援 中型應用、結果推送場景
WebSocket 雙向通訊、低延遲 維護複雜、需要心跳機制 大型應用、高即時性需求
HTTP/2 推送 多路復用、頭部壓縮 需要 HTTPS、瀏覽器支援 現代化應用、效能優先

資料儲存方案選擇

儲存方案 寫入效能 查詢效能 一致性 擴展性 成本
PostgreSQL 垂直
MongoDB 最終 水平
Cassandra 極高 可調 水平
DynamoDB 最終 自動 按用量

防重複投票機制層級

diagram5

實戰經驗與教訓

常見架構陷阱

  1. 過度依賴資料庫鎖

    • 錯誤:使用悲觀鎖確保計數準確性
    • 正確:使用樂觀鎖配合重試機制
    • 原因:悲觀鎖在高併發下會成為嚴重瓶頸
  2. 忽視快取一致性

    • 錯誤:直接更新快取中的計數
    • 正確:使用 Cache Aside 模式或事件驅動更新
    • 原因:避免快取與資料庫資料不一致
  3. 即時推送連線管理不當

    • 錯誤:為每個客戶端維持獨立的推送連線
    • 正確:使用連線池和訊息廣播機制
    • 原因:減少伺服器資源消耗,提升擴展性

業界案例分析

案例:Twitter 投票功能的架構演進 參考文章

發展歷程:

  1. 初期(2015-2016)

    • 架構特點:投票作為推文的附加功能,使用既有的推文儲存架構
    • 技術:Gizzard(分片框架)+ Manhattan(分散式資料庫)
    • 規模:每日數百萬投票,峰值 QPS 約 10,000
  2. 成長期(2017-2019)

    • 主要改進:獨立的投票服務、引入串流處理進行即時統計
    • 遇到的挑戰:熱門投票造成的熱點問題、跨區域資料同步延遲
    • 解決方案:實作投票結果的分層快取、使用 EventBus 進行非同步處理
  3. 成熟期(2020-現在)

    • 當前架構特點:完全微服務化、多層快取架構、機器學習反作弊
    • 技術棧:Kafka Streams 即時處理、Hadoop 離線分析、GraphQL API
    • 未來發展:探索區塊鏈技術確保投票透明度

關鍵學習點:

  • 熱點問題不可避免,需要從架構層面設計應對策略
  • 投票防作弊需要多維度偵測,包括行為模式、設備指紋、社交圖譜分析
  • 即時性和準確性的平衡點會隨業務發展不斷調整

監控與維護策略

關鍵指標體系

技術指標:

  • API 回應時間 P99(目標值 < 200ms)
  • 投票處理延遲(目標值 < 1s)
  • WebSocket 連線成功率(目標值 > 99.5%)
  • 資料庫連線池使用率(目標值 < 70%)
  • 快取命中率(目標值 > 90%)

業務指標:

  • 每分鐘投票數(監控異常峰值)
  • 重複投票攔截率(目標值 > 95%)
  • 使用者參與率(活躍使用者/總使用者)
  • 投票完成率(完成投票/開始投票)
  • 地理分布熱力圖(識別異常集中)

維護最佳實踐

  1. 自動化策略

    • 自動擴容:基於 CPU 和連線數的 Auto Scaling
    • 自動故障轉移:健康檢查失敗自動切換
    • 自動備份:每小時增量備份,每日全量備份
  2. 監控告警

    • 分級告警:P0 立即處理,P1 15分鐘內回應
    • 智慧告警:基於歷史資料的異常檢測
    • 告警聚合:避免告警風暴
  3. 持續優化

    • A/B 測試新的防作弊演算法
    • 定期壓力測試,驗證系統容量
    • 資料分析找出系統瓶頸

總結

核心要點回顧

今天我們深入探討了線上投票系統的設計,從簡單的 MVP 版本演進到支援百萬使用者的企業級架構。這個過程中,我們學習到幾個關鍵點:

  • 即時性與一致性的平衡:不是所有場景都需要強一致性,合理使用最終一致性可以大幅提升系統效能
  • 防作弊是系統工程:需要從客戶端到資料層的多層防護,沒有銀彈
  • 架構演進要循序漸進:不要過早優化,根據實際需求逐步演進
  • 技術選型要考慮團隊能力:最先進的技術不一定是最合適的

設計原則提煉

  1. 資料準確性優先於即時性:寧可延遲幾秒顯示,也不能顯示錯誤的投票結果
  2. 防護機制分層部署:每一層攔截不同類型的作弊行為,提高整體防護效果
  3. 優雅降級保證可用性:即時推送失敗時自動降級為輪詢,確保基本功能可用
  4. 監控先於優化:沒有資料支撐的優化都是臆測,先建立完善的監控體系
  5. 設計時考慮營運:架構設計不只是技術問題,還要考慮團隊的營運能力

下期預告

明天我們將進入短網址服務系統。同時也是系統設計面試中的經典題目,看似簡單的功能背後,隱藏著許多有趣的設計挑戰:如何設計高效的編碼演算法?如何處理每秒數萬次的跳轉請求?如何防止惡意網址?如何實現自定義短網址?

短網址系統會讓我們深入探討分散式 ID 生成、快取策略優化、以及如何在儲存成本與查詢效能間取得平衡。準備好了嗎?讓我們一起揭開短網址服務的設計奧秘!

參考資源


上一篇
個人部落格系統 - 從靜態到動態的架構演進
系列文
30個系統設計實戰:全端工程師的架構修煉3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言